#include <string.h>
#include <iostream>
using std::cout;
using std::endl;

class CowString{
public:
    CowString()
    :_pstr(new char[1 + 4]() + 4)
    {
        //表示的含义是申请空间的时候多申请4字节
        //申请出来的空间地址需要向后偏移4个字节
        //因为从第4个字节开始存放的才是字符
        //处理引用计数应该 - 4
        //此时引用计数应该是1
        //1._pstr地址 - 4 * 步长 = _pstr地址减4字节
        //2.(int *)强转为int *,只能够操作前4字节
        //3.*进行解引用操作：通过指针间接去修改所指向的值
        *(int *)(_pstr - 4) = 1;
    }
    CowString(const char * pstr)
    :_pstr(new char[strlen(pstr) + 1 + 4]() + 4)
    {
        strcpy(_pstr, pstr);
        *(int *)(_pstr - 4) = 1;
    }

    //拷贝构造
    //执行的是浅拷贝操作
    //引用计数 + 1
    CowString(const CowString & rhs)
    :_pstr(rhs._pstr)
    {
        ++*(int *)(_pstr -4);
    }

    //赋值运算符函数 之前深拷贝时操作步骤
    //1.自赋值判断
    //2.回收左操作数申请的堆空间
    //3.执行深拷贝
    //4.返回结果
    //浅拷贝
    //1....
    //2.左操作数的引用计数-1，如果=0需要回收空间
    //3.右操作数的引用计数+1
    //4.返回结果
    CowString & operator=(const CowString & rhs){
        if(this != &rhs){
            if(--*(int *)(_pstr - 4) == 0){
                delete [] (_pstr - 4);
            }
            //右操作数的引用计数+1
            _pstr = rhs._pstr;
            ++*(int *)(_pstr - 4);
        }
        return *this;
    }

    //下标访问运算符
    //区分读写操作i
    //只关注写，写出后面的逻辑
    //关注当前的字符串的引用计数是否等于1，如果等于1，直接修改即可
    //如果引用计数大于1，则需要深拷贝一份出来，在新的地方进行修改
    //此时原来的引用计数 - 1，新的引用计数 = 1
    //str2[0] = 'H';
    //Obj operator[](size_t index){}
    //cout << str[0] << endl;=====》转换
    //cout << obj << endl; 输出流运算符重载
    //str[0] = 'H'=========>转换   obj = 'H'; 赋值运算符重载
    char & operator[](size_t index){
        if(index >= size()){
            static char nullchar = '\0';
            return nullchar;
        }
        if(refcount() > 1){
            char * temp = new char[strlen(_pstr) + 1 + 4]() + 4;
            strcpy(temp, _pstr);
            //现在关联的旧的引用计数-1
            --*(int *)(_pstr - 4);
            _pstr = temp;
            *(int *)(_pstr - 4) = 1;
        }
        return _pstr[index];
    }

    ~CowString(){
        if(--*(int *)(_pstr - 4) == 0){
            delete [] (_pstr - 4);
        }
    }

    int refcount() const {
        return *(int *)(_pstr - 4);
    }
    const char * c_str() const {
        return _pstr;
    }
    int size() const {
        return strlen(_pstr);
    }


private:
    //只需要这一个数据成员即可
    //引用计数和字符串存放在一起
    //引用计数存在前面，字符存在后面
    char * _pstr;
};

class Point{
public:
    Point(int x, int y)
    :_x(x)
    ,_y(y)
    {

    }

private:
    int _x;
    int _y;
};
void test2(){
    Point * pt = new Point(1, 2);
    cout << sizeof(Point) << endl;
    //point对象占据8个字节
    //所以pt指针指向了当前point首地址，并且该指针能够操作的空间是8字节大小
    //将上述的指针进行强转转为int*
    int * pInt = (int *)pt;
    cout << *pInt << endl;
    cout << *(pInt + 1) << endl;
}

void test(){
    CowString str1;
    cout << str1.c_str() << endl;
    cout << str1.refcount() << endl;
    CowString str2("hello");
    cout << str2.c_str() << endl;
    cout << str2.refcount() << endl;
    str1 = str2;
    cout << str2.c_str() << endl;
    cout << str2.refcount() << endl;
    cout << str1.c_str() << endl;
    cout << str1.refcount() << endl;

}

void test3(){
    CowString str("hello");
    CowString str2 = str;
    cout << str.c_str() << endl;
    cout << str2.c_str() << endl;
    cout << str.refcount() << endl;
    cout << str2.refcount() << endl;
}

void test4(){
    CowString str("hello");
    CowString str2 = str;
    cout << str.refcount() << endl;
    cout << str2.refcount() << endl;
    cout << str.c_str() << endl;
    cout << str2.c_str() << endl;
    cout << "===========" << endl;
    str2[0] = 'H';
    cout << str.refcount() << endl;
    cout << str2.refcount() << endl;
    cout << str.c_str() << endl;
    cout << str2.c_str() << endl;
}
void test5(){
    CowString str("hello");
    //CowString str2("hello");
    //
    CowString str2 = str;
    cout << str2[0] << endl;
    cout << str.refcount() << endl;
    cout << str2.refcount() << endl;
    cout << str.c_str() << endl;
    cout << str2.c_str() << endl;
}

int main()
{
    test5();
    return 0;
}

